package hermesV3.services.Space;

import it.unicam.cs.cosy.bioshape.core.components.Entity;
import it.unicam.cs.cosy.bioshape.core.components.LocalCoordinator;
import it.unicam.cs.cosy.bioshape.core.components.Pair;
import it.unicam.cs.cosy.bioshape.core.shapes.Bounds;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

import hermesV3.agent.Identifier;

/**
 * Handles the {@link LocalCoordinator} requests regarding the {@link Entity}
 * ownership among the {@link LocalCoordinator} and check which
 * {@link LocalCoordinator} see the {@link Entity} as phantom entities.
 * 
 * @author Matteo Micheletti, Federico Buti
 * 
 */
public class LocalHandler implements
		Callable<Map<Identifier, ArrayList<Entity>>> {

	private Identifier sender;
	private ArrayList<Entity> shapedEntities;
	private Map<Identifier, ArrayList<Entity>> phantomEntities;
	private Pair<Identifier, Bounds>[] lcBounds;

	/**
	 * Builds a {@link LocalHandler} callable thread. It checks the entities of
	 * the specific {@code LocalCoordinator} against the bounds of all the other
	 * {@code LocalCoordinator}s. If intersection is found, then the specific
	 * entity is set as a <i>phantom</i> entity for that coordinator. TODO: It
	 * would be possible to improve the check by exploit locals <i>topology</i>:
	 * if a shape does not intersect a coordinator it is not likely to intersect
	 * others.
	 * 
	 * @param shapedEntities
	 *            List of entities managed from the local
	 * @param sender
	 *            The {@code LocalCoordinator} which issued the spatial check
	 * @param lcBounds
	 *            Array of {@code Pair} object containing the
	 *            {@code Identifier} and the corresponding {@code Bounds}
	 *            object for each {@code LocalCoordinator}
	 * @see it.unicam.cs.cosy.bioshape.core.components.LocalCoordinator
	 */
	public LocalHandler(ArrayList<Entity> shapedEntities, Identifier sender,
			Pair<Identifier, Bounds>[] lcBounds) {
		this.shapedEntities = shapedEntities;
		this.sender = sender;
		this.lcBounds = lcBounds;
		phantomEntities = new HashMap<Identifier, ArrayList<Entity>>(
				lcBounds.length);
		for (Pair<Identifier, Bounds> id : lcBounds) {
			phantomEntities.put(id.getE1(), new ArrayList<Entity>());
		}
	}

	/**
	 * Given the list of entities not completely contained in the passed
	 * {@code LocalCoordinator}, each entity is checked against all the locals
	 * bounds (except its owner). If an intersection occurs the entity is
	 * considered phantom for the involved coordinator.
	 */
	private void checkPhantomsOwnerships() {
		for (Entity se : shapedEntities) {
			for (int j = 0; j < lcBounds.length; j++) {
				if (lcBounds[j].getE1().equals(sender))
					continue;
				if (se.getBounds().intersect(lcBounds[j].getE2()))
					phantomEntities.get(lcBounds[j].getE1()).add(se);
			}
		}
	}

	@Override
	public Map<Identifier, ArrayList<Entity>> call() throws Exception {
		checkPhantomsOwnerships();
		return phantomEntities;
	}
}